/*
 * A PostgreSQL Index Access Method Implement Framework
 */

#include "postgres.h"

#include "access/genam.h"
#include "access/itup.h"
#include "catalog/index.h"
#include "commands/vacuum.h"

/* 使得系统将各函数当作 V1 扩展函数看待，很重要！*/

PG_FUNCTION_INFO_V1(hellobuild);
PG_FUNCTION_INFO_V1(helloinsert);
PG_FUNCTION_INFO_V1(hellobeginscan);
PG_FUNCTION_INFO_V1(hellogettuple);
PG_FUNCTION_INFO_V1(hellogetmulti);
PG_FUNCTION_INFO_V1(hellorescan);
PG_FUNCTION_INFO_V1(helloendscan);
PG_FUNCTION_INFO_V1(hellomarkpos);
PG_FUNCTION_INFO_V1(hellorestrpos);
PG_FUNCTION_INFO_V1(hellobulkdelete);
PG_FUNCTION_INFO_V1(hellovacuumcleanup);
PG_FUNCTION_INFO_V1(hellooptions);


extern Datum hellobuild(PG_FUNCTION_ARGS);
extern Datum helloinsert(PG_FUNCTION_ARGS);
extern Datum hellobeginscan(PG_FUNCTION_ARGS);
extern Datum hellogettuple(PG_FUNCTION_ARGS);
extern Datum hellogetmulti(PG_FUNCTION_ARGS);
extern Datum hellorescan(PG_FUNCTION_ARGS);
extern Datum helloendscan(PG_FUNCTION_ARGS);
extern Datum hellomarkpos(PG_FUNCTION_ARGS);
extern Datum hellorestrpos(PG_FUNCTION_ARGS);
extern Datum hellobulkdelete(PG_FUNCTION_ARGS);
extern Datum hellovacuumcleanup(PG_FUNCTION_ARGS);
extern Datum hellooptions(PG_FUNCTION_ARGS);

static void hellobuildCallback(Relation index,
				  HeapTuple htup,
				  Datum *values,
				  bool *isnull,
				  bool tupleIsAlive,
				  void *state);

PG_MODULE_MAGIC;

Datum
hellobuild(PG_FUNCTION_ARGS)
{
	Relation	heap = (Relation) PG_GETARG_POINTER(0);
	Relation	index = (Relation) PG_GETARG_POINTER(1);
	IndexInfo  *indexInfo = (IndexInfo *) PG_GETARG_POINTER(2);
	IndexBuildResult *result;
	unsigned long reltuples = 0;


	if (RelationGetNumberOfBlocks(index) != 0)
		elog(ERROR, "index \"%s\" already contains data",
			 RelationGetRelationName(index));

	reltuples = IndexBuildHeapScan(heap, index, indexInfo,
								   hellobuildCallback, (void *) &reltuples);

	result = (IndexBuildResult *) palloc(sizeof(IndexBuildResult));

	result->heap_tuples  = reltuples;
	result->index_tuples = reltuples;

	PG_RETURN_POINTER(result);
}

static void
hellobuildCallback(Relation index,
				  HeapTuple htup,
				  Datum *values,
				  bool *isnull,
				  bool tupleIsAlive,
				  void *state)
{
	unsigned long *prelcnt = (unsigned long *)state;
	IndexTuple	itup;

	/* form an index tuple and point it at the heap tuple */
	itup = index_form_tuple(RelationGetDescr(index), values, isnull);
	itup->t_tid = htup->t_self;

	if (IndexTupleHasNulls(itup))
	{
		pfree(itup);
		return;
	}

	/*
	_hash_doinsert(index, itup);
	*/

	*prelcnt += 1;

	pfree(itup);
}

Datum
helloinsert(PG_FUNCTION_ARGS)
{
	Relation	rel = (Relation) PG_GETARG_POINTER(0);
	Datum	   *values = (Datum *) PG_GETARG_POINTER(1);
	bool	   *isnull = (bool *) PG_GETARG_POINTER(2);
	ItemPointer ht_ctid = (ItemPointer) PG_GETARG_POINTER(3);

#ifdef NOT_USED
	Relation	heapRel = (Relation) PG_GETARG_POINTER(4);
	bool		checkUnique = PG_GETARG_BOOL(5);
#endif
	IndexTuple	itup;

	/* generate an index tuple */
	itup = index_form_tuple(RelationGetDescr(rel), values, isnull);
	itup->t_tid = *ht_ctid;

	if (IndexTupleHasNulls(itup))
	{
		pfree(itup);
		PG_RETURN_BOOL(false);
	}

	/*
	_hash_doinsert(rel, itup);
	*/

	pfree(itup);

	PG_RETURN_BOOL(true);
}


Datum
hellogettuple(PG_FUNCTION_ARGS)
{
	IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
	ScanDirection dir = (ScanDirection) PG_GETARG_INT32(1);
	Relation	rel = scan->indexRelation;
#ifdef NOT_USED	
	Page		page;
	OffsetNumber offnum;
#endif	
	bool		res = false;

	PG_RETURN_BOOL(res);
}


Datum
hellogetmulti(PG_FUNCTION_ARGS)
{
	IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
	ItemPointer tids = (ItemPointer) PG_GETARG_POINTER(1);
	int32		max_tids = PG_GETARG_INT32(2);
	int32	   *returned_tids = (int32 *) PG_GETARG_POINTER(3);
	Relation	rel = scan->indexRelation;
	bool		res = false;

	PG_RETURN_BOOL(res);
}


Datum
hellobeginscan(PG_FUNCTION_ARGS)
{
	Relation	rel = (Relation) PG_GETARG_POINTER(0);
	int			keysz = PG_GETARG_INT32(1);
	ScanKey		scankey = (ScanKey) PG_GETARG_POINTER(2);
	IndexScanDesc scan;

	scan = RelationGetIndexScan(rel, keysz, scankey);

	PG_RETURN_POINTER(scan);
}

Datum
hellorescan(PG_FUNCTION_ARGS)
{
	IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
	ScanKey		scankey = (ScanKey) PG_GETARG_POINTER(1);
	Relation	rel = scan->indexRelation;

	PG_RETURN_VOID();
}

Datum
helloendscan(PG_FUNCTION_ARGS)
{
	IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
	Relation	rel = scan->indexRelation;

	scan->opaque = NULL;

	PG_RETURN_VOID();
}

Datum
hellomarkpos(PG_FUNCTION_ARGS)
{
	IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
	Relation	rel = scan->indexRelation;

	PG_RETURN_VOID();
}

Datum
hellorestrpos(PG_FUNCTION_ARGS)
{
	IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
	Relation	rel = scan->indexRelation;

	PG_RETURN_VOID();
}

Datum
hellobulkdelete(PG_FUNCTION_ARGS)
{
	IndexVacuumInfo *info = (IndexVacuumInfo *) PG_GETARG_POINTER(0);
	IndexBulkDeleteResult *stats = (IndexBulkDeleteResult *) PG_GETARG_POINTER(1);
	IndexBulkDeleteCallback callback = (IndexBulkDeleteCallback) PG_GETARG_POINTER(2);
	void	   *callback_state = (void *) PG_GETARG_POINTER(3);
	stats->num_index_tuples = 0;
	stats->tuples_removed += 0;
	/* hashvacuumcleanup will fill in num_pages */

	PG_RETURN_POINTER(stats);
}

Datum
hellovacuumcleanup(PG_FUNCTION_ARGS)
{
	IndexVacuumInfo *info = (IndexVacuumInfo *) PG_GETARG_POINTER(0);
	IndexBulkDeleteResult *stats = (IndexBulkDeleteResult *) PG_GETARG_POINTER(1);
	Relation	rel = info->index;
	BlockNumber num_pages;

	/* If hashbulkdelete wasn't called, return NULL signifying no change */
	if (stats == NULL)
		PG_RETURN_POINTER(NULL);

	/* update statistics */
	num_pages = RelationGetNumberOfBlocks(rel);
	stats->num_pages = num_pages;

	PG_RETURN_POINTER(stats);
}


Datum
hellooptions(PG_FUNCTION_ARGS)
{
	Datum		reloptions = PG_GETARG_DATUM(0);
	bool		validate = PG_GETARG_BOOL(1);

	PG_RETURN_NULL();
}

void
hello_redo(XLogRecPtr lsn, XLogRecord *record)
{
	elog(PANIC, "hash_redo: unimplemented");
}

void
hello_desc(StringInfo buf, uint8 xl_info, char *rec)
{
}
